package com.boot.app.base.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.boot.app.base.domain.BaseUser;
import com.boot.app.base.domain.BaseUserAddition;
import com.boot.app.base.dto.PasswordDto;
import com.boot.app.base.dto.UserDetailsImpl;
import com.boot.app.base.dto.UserDTO;
import com.boot.app.base.service.*;
import com.boot.exception.ApiException;
import com.boot.util.SessionUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

@Service
public class BaseUserDetailsServiceImpl implements UserDetailsService, IBaseUserDetailsService {

    private final SessionUtil sessionUtil;

    private final StringRedisTemplate stringRedisTemplate;

    private final PasswordEncoder passwordEncoder;

    private final IBaseUserService userService;

    private final IBaseUserAdditionService userAdditionService;

    @Autowired
    BaseUserDetailsServiceImpl(
        SessionUtil sessionUtil,
        StringRedisTemplate stringRedisTemplate,
        PasswordEncoder passwordEncoder,
        IBaseUserService userService,
        IBaseUserAdditionService userAdditionService
    ){
        this.sessionUtil = sessionUtil;
        this.stringRedisTemplate = stringRedisTemplate;
        this.passwordEncoder = passwordEncoder;
        this.userService = userService;
        this.userAdditionService = userAdditionService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        BaseUser user = userService.getByUsername(username);
        if(user == null) throw new UsernameNotFoundException("用户不存在");
        return new UserDetailsImpl(user);
    }

    @Transactional
    @Override
    public void saveOrUpdateUserDto(UserDTO userDto) {
        BaseUser user = new BaseUser();
        BaseUserAddition userAddition = new BaseUserAddition();
        if(ObjectUtil.isNull(userDto.getId())){//新增用户
            BaseUser tmp = userService.getUserByUsername(userDto.getUsername());
            Assert.isNull(tmp, "用户名已存在。");

            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            String password = operations.get("PASSWORD.RESET");
            user.setPassword(passwordEncoder.encode(password));
        }
        BeanUtils.copyProperties(userDto, user);
        BeanUtils.copyProperties(userDto, userAddition);
        userService.saveOrUpdate(user);

        userAddition.setUserId(user.getId());
        userAdditionService.saveOrUpdate(userAddition);
    }

    @Override
    public void updatePassword(PasswordDto passwordDto) {
        BaseUser user = userService.getById(sessionUtil.getUserId());
        if(!passwordEncoder.matches(passwordDto.getOldPassword(), user.getPassword())){
            throw new ApiException("旧密码错误");
        }
        if(!passwordDto.getNewPassword().equals(passwordDto.getConfirmPassword())){
            throw new ApiException("两次输入的密码不一致");
        }
        if(passwordDto.getNewPassword().equals(passwordDto.getOldPassword())){
            throw new ApiException("新密码不能与旧密码一致");
        }
        user.setPassword(passwordEncoder.encode(passwordDto.getNewPassword()));
        userService.updateById(user);
    }

    @Override
    public void resetPassword(long id) {
        BaseUser user = userService.getById(id);
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        user.setPassword(passwordEncoder.encode(operations.get("PASSWORD.RESET")));
        userService.updateById(user);
    }

}
